/**
 * Copyright 2014 wasu.com
 *
 *
 * Create on 2014-12-29 下午4:38:45
 */
package xj.toolkit.jms;

import java.io.UnsupportedEncodingException;

import org.apache.commons.collections4.Transformer;
import org.apache.commons.lang3.ArrayUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import xj.toolkit.bean.CommonsHeader;
import xj.toolkit.bean.json.JSONSignal;
import xj.toolkit.bean.util.meta.Int2TypeMetainfo;
import xj.toolkit.util.ByteUtils;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Preconditions;
import com.google.common.primitives.Ints;
import com.google.common.primitives.Longs;
import com.google.common.primitives.Shorts;

/**
 * 将二进制数据转换为对象。
 * 
 * @author <a href="mailto:caoxiaojian@wasu.com>xiaojian.cao</a>
 * 
 */
public class Bytes2MsgTrnasformer implements Transformer<byte[], JSONSignal> {

	private static final Logger logger = LoggerFactory.getLogger(Bytes2MsgTrnasformer.class);

	// 大于1M的数据包可能是问题数据包
	private int maxMessageLength = 1024 * 1024;

	private int dumpBytes = 256;
	private boolean isDebugEnabled;

	private Int2TypeMetainfo jsonTypeMetainfo;

	/**
	 * @see stc.skymobi.util.Transformer#transform(java.lang.Object)
	 */
	@Override
	public JSONSignal transform(byte[] bytes) {
		Preconditions.checkArgument(bytes != null && bytes.length > 0);

		if (bytes.length < CommonsHeader.HEADER_LENGTH) {
			return null;
		}

		if (logger.isDebugEnabled() && isDebugEnabled) {
			logger.debug("parse header... try parse...");
		}

		byte[] headerBytes = new byte[CommonsHeader.HEADER_LENGTH];
		System.arraycopy(bytes, 0, headerBytes, 0, CommonsHeader.HEADER_LENGTH);
		if (logger.isDebugEnabled() && isDebugEnabled) {
			logger.debug("header raw bytes -->");
			logger.debug(ByteUtils.bytesAsHexString(headerBytes, dumpBytes));
		}

		CommonsHeader header = new CommonsHeader();
		header.setBasicVer(headerBytes[0]);
		header.setPayloadType(Shorts.fromByteArray(ArrayUtils.subarray(headerBytes, 1, 3)));
		header.setType(Shorts.fromByteArray(ArrayUtils.subarray(headerBytes, 3, 5)));

		header.setIdBytesLen(Shorts.fromByteArray(ArrayUtils.subarray(headerBytes, 5, 7)));

		byte[] idBytes = ArrayUtils.subarray(headerBytes, 7, header.getIdBytesLen() + 7);
		header.setIdBytes(idBytes);

		header.setReserved(Ints.fromByteArray(
				ArrayUtils.subarray(headerBytes, header.getIdBytesLen() + 7, header.getIdBytesLen() + 11)));
		header.setMessageCode(Ints.fromByteArray(
				ArrayUtils.subarray(headerBytes, header.getIdBytesLen() + 11, header.getIdBytesLen() + 15)));
		header.setMessageLength(Ints.fromByteArray(
				ArrayUtils.subarray(headerBytes, header.getIdBytesLen() + 15, header.getIdBytesLen() + 19)));

		if (logger.isDebugEnabled() && isDebugEnabled) {
			logger.debug("header-->" + header);
		}

		if (header.getMessageLength() < 0) {
			logger.error("header.messageLength ({}) < 0,\r\ndump bytes received:\r\n{}", header.getMessageLength(),
					ByteUtils.bytesAsHexString(headerBytes, dumpBytes));
			throw new RuntimeException("messageLength too large:" + header.getMessageLength());
		}
		if (maxMessageLength > 0) {
			if (header.getMessageLength() > maxMessageLength) {
				logger.error(
						"header.messageLength (" + header.getMessageLength() + ") exceed maxMessageLength["
								+ maxMessageLength + "].\r\ndump bytes received:\r\n{}",
						ByteUtils.bytesAsHexString(headerBytes, dumpBytes));
				return null;
			}
		}

		final byte[] contentBytes = new byte[header.getMessageLength()];
		System.arraycopy(bytes, CommonsHeader.HEADER_LENGTH, contentBytes, 0, header.getMessageLength());

		if (logger.isTraceEnabled() && isDebugEnabled) {
			logger.trace("body raw bytes \r\n{}", ByteUtils.bytesAsHexString(contentBytes, dumpBytes));
		}

		if (100 == header.getBasicVer()) {
			int msgCode = header.getMessageCode();
			Class<?> clazz = jsonTypeMetainfo.find(msgCode);
			if (clazz == null) {
				logger.error("invalid msgcode {" + msgCode + "}");
				return null;
			}

			Object outObj = null;
			try {
				outObj = JSON.parseObject(new String(contentBytes, "UTF-8"), clazz);
			} catch (UnsupportedEncodingException e) {
				logger.error("Unsupport encoding[utf-8].", e);
				return null;
			}

			if (null != outObj) {
				return (JSONSignal) outObj;
			}
		} else {
			logger.error("invalid basic ver, while header is {}", header);
			logger.error("raw body bytes is {}", ByteUtils.bytesAsHexString(bytes, header.getMessageLength()));
		}

		return null;
	}

	/**
	 * @return the maxMessageLength
	 */
	public int getMaxMessageLength() {
		return maxMessageLength;
	}

	/**
	 * @param maxMessageLength
	 *            the maxMessageLength to set
	 */
	public void setMaxMessageLength(int maxMessageLength) {
		this.maxMessageLength = maxMessageLength;
	}

	/**
	 * @return the dumpBytes
	 */
	public int getDumpBytes() {
		return dumpBytes;
	}

	/**
	 * @param dumpBytes
	 *            the dumpBytes to set
	 */
	public void setDumpBytes(int dumpBytes) {
		this.dumpBytes = dumpBytes;
	}

	/**
	 * @return the isDebugEnabled
	 */
	public boolean isDebugEnabled() {
		return isDebugEnabled;
	}

	/**
	 * @param isDebugEnabled
	 *            the isDebugEnabled to set
	 */
	public void setDebugEnabled(boolean isDebugEnabled) {
		this.isDebugEnabled = isDebugEnabled;
	}

	/**
	 * @return the jsonTypeMetainfo
	 */
	public Int2TypeMetainfo getJsonTypeMetainfo() {
		return jsonTypeMetainfo;
	}

	/**
	 * @param jsonTypeMetainfo
	 *            the jsonTypeMetainfo to set
	 */
	public void setJsonTypeMetainfo(Int2TypeMetainfo jsonTypeMetainfo) {
		this.jsonTypeMetainfo = jsonTypeMetainfo;
	}

}
